package cn.tedu.charging.device.warm;

import cn.tedu.charging.common.pojo.po.ChargingStationPO;
import cn.tedu.charging.device.mapper.StationMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisCommands;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 这个类可以实现 整个应用程序 对应设备数据
 * 所有所需的预热数据加载
 */
@Component //保证是容器的Bean对象
@Slf4j
//@Order(100) 用于调整预热执行顺序，数字越小，先执行
public class WarmUpStarter implements ApplicationRunner{
    @Autowired
    private StationMapper stationMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        String lock="charging:warm:up.lock";
        //set lock value NX true false
        Boolean haveLock = redisTemplate.opsForValue().setIfAbsent(lock, "");
        if (!haveLock){
            log.info("线程没有抢到锁,不执行预热");
            return;
        }
        Integer batchSize=2;
        log.info("缓存预热开始执行,按批次读取写入,每批:{}",batchSize);
        //1.读取数据总量
        Long total=getStataionCount();
        if (total<batchSize){
            log.info("总量不足{}",batchSize);
            //只需要一批
            List<ChargingStationPO> stationPos=getAllStations();
            saveGeoStationId(stationPos);
        }else{
            log.info("总量超过{},{}",batchSize,total);
            //循环写入redis
            Long loopTime=total%batchSize==0?total/batchSize:total/batchSize+1;
            for (Long i=0l;i<loopTime;i++){
                //select * from station limit i*1000,1000;
                List<ChargingStationPO> stationPos=getStationsLimit(i,batchSize);
                saveGeoStationId(stationPos);
            }
        }
    }

    private List<ChargingStationPO> getStationsLimit(Long i, int batchSize) {
        //select * from station limit i*1000,batchSize;
        QueryWrapper queryWrapper=new QueryWrapper();
        queryWrapper.last("limit "+i*batchSize+","+batchSize);
        return stationMapper.selectList(queryWrapper);
    }

    private Long getStataionCount() {
        //select count(0) from station
        return stationMapper.selectCount(null);
    }

    private void saveGeoStationId(List<ChargingStationPO> stationPos) {
        //1.key=charging:stations:all 放到common的常量中 最合理的
        String key="charging:stations:all";
        //2.geoadd lng1 lat1 id1 lng2 lat2 id2 .... 节省网络往返时间 一次性写入多个元素
        //2.1 拿到操作geo客户端子对象
        GeoOperations geoOps = redisTemplate.opsForGeo();
        //2.2 组织一个List<GeoLocation> 集合一次性写入 stream().map
        List<RedisGeoCommands.GeoLocation> geoLocations=new ArrayList<>();
        for (ChargingStationPO po : stationPos) {
            Integer member = po.getId();
            double xLng = po.getStationLng().doubleValue();
            double yLat = po.getStationLat().doubleValue();
            RedisGeoCommands.GeoLocation geoLocation =
                    new RedisGeoCommands.GeoLocation<>(member, new Point(xLng, yLat));
            geoLocations.add(geoLocation);
        }
        /*List<RedisGeoCommands.GeoLocation> geoLocations = stationPos.stream().map(po -> {
            //映射计算过程 stationPo 变成GeoLocation的过程
            Integer member = po.getId();
            double xLng = po.getStationLng().doubleValue();
            double yLat = po.getStationLat().doubleValue();
            RedisGeoCommands.GeoLocation geoLocation =
                    new RedisGeoCommands.GeoLocation<>(member, new Point(xLng, yLat));
            //将计算结果返回
            return geoLocation;
        }).collect(Collectors.toList());*/
        //2.3调用redis客户端api写入redis
        geoOps.add(key,geoLocations);


    }

    private List<ChargingStationPO> getAllStations() {
        //charging_stations select * from charging_stations
        return stationMapper.selectList(null);
    }
}